/**
 *  Pryn.js
 *  @see       http://0-oo.net/sbox/javascript/pryn-js-css
 *  @version   0.2.4
 *  @copyright 2007-2009 dgbadmin@gmail.com
 *  @license   http://0-oo.net/pryn/MIT_license.txt (The MIT license)
 *
 *  連携するCSSクラス
 *    (a, form).open            別Windowで開く
 *    (a, form).same-window     同じWindowで開く
 *    (input, textarea).focused 入力状態になった要素
 *    (input, textarea).ready   デフォルトでfocusを当てる要素
 *    (input, textarea).hint    ヒント付テキストボックス
 *                               ・入力後のclassは ex-hint になる
 *                               ・ヒントはtitle属性にセットしておく
 *    (input, textarea).copy    コピー用テキスト
 *    (input, img).clickable    クリックできる要素
 *    table.stripes             シマシマにしたい表
 *    tr.stripe                 シマシマにされた行
 */


/** IE高速化 @see http://d.hatena.ne.jp/amachang/20071010/1192012056 */
/*@cc_on _d = document; eval("var document = _d")@*/


/***** 組み込みオブジェクトの拡張 *****/

/**
 *  文字列のtrim
 *  @return trim後の文字列
 */
String.prototype.trim = function() {
    return this.replace(/(^\s+|\s+$)/g, "");
};
/**
 *  1単語を表す正規表現を取得する
 *  @return 正規表現オブジェクト
 */
String.prototype.word2regexp = function() {
    return new RegExp("(^|\\s)" + this + "(\\s|$)", "ig");
};
/**
 *  子要素全てに関数の処理を実行する
 *  @param  Object  it  子要素をループ処理できるオブジェクト
 */
Function.prototype.foreach = function(it) {
    for (var i = 0, len = it.length; i < len; i++) {
        this(it[i]);
    }
};


/***** グローバル関数/オブジェクト *****/

/**
 *  Firebugが無い場合はconsole.log()を空振りさせる
 */
window.console = window.console || {log: function(s){}, dummy: true};

/**
 *  html要素をidで取得する（既に$()があればそちらを使う）
 *  @param  String  id
 *  @retrun html要素
 */
var $ = $ || function(id) {
    return document.getElementById(id);
};
/**
 *  html要素をタグ名で取得する
 *  @param  String  tagName
 *  @retrun NodeList
 */
function $T(tagName) {
    return document.getElementsByTagName(tagName);
}
/**
 *  ページを遷移する
 *  @param  String  url
 */
function go(url) {
    location.href = url;
}


/***** Pryn *****/

var Pryn = {
    /** ページ表示時のユーザビリティ向上用の設定 */
    Init: {
        /** 別ドメインへの遷移を別Windowで開くかどうか */
        keepThisSite: true,

        /** formの二重送信防止を解除するまでの時間（秒） */
        frozenTime: 5,
        /** formの二重送信防止対象 */
        frozen: [],

        /** label要素と対応させるidの番号 */
        labelForIdNo: 0,
        /** label要素と対応させるidのプリフィクス */
        labelForIdPrefix: "pryn-label-for-id-",

        /** imageボタンの配列 */
        inputImages: []
    }
};

/**
 *  イベントを追加する
 *  @param  HTMLElement elm
 *  @param  String      eventName
 *  @param  Function    fnc
 */
Pryn.addEvent = (function(){
    if (window.attachEvent) {
        return function(elm, eventName, fnc){
            //IEでもfunction内でthisで自分（elm）を参照できるようにする
            elm.attachEvent("on" + eventName, function(){ fnc.call(elm); });
        };
    }
    //Safari3.0.x以前はDOMContentLoadedに未対応
    var webKit = navigator.userAgent.match(/AppleWebKit\/([0-9]+)/);
    var oldWebKit = (webKit && webKit[1] < 525);
    return function(elm, eventName, fnc){
        if (elm == window && eventName == "load" && !oldWebKit) {
            document.addEventListener("DOMContentLoaded", fnc, false);
        } else {
            elm.addEventListener(eventName, fnc, false);
        }
    };
})();

/**
 *  リンクのURLからドメイン部分を取り出す
 *  @param  HTMLElement link
 *  @return メイン（ドメインを含んでいない場合はnull）
 */
Pryn.getDomain = function(link) {
    var href = link.href;
    if (href.match(/^http(|s):/i)) {
        return href.split("/")[2];
    } else {
        return null;
    }
};


/***** Pryn.ClassAccessor *****/

/**
 *  html要素のclass属性へのアクセサクラス
 *  @param  HTMLElement elm
 */
Pryn.ClassAccessor = function(elm) {
    /**
     *  html要素にclassがあるか確かめる
     *  @param  String  cName
     */
    this.hasClass = function(cName) {
        var cn = elm.className;
        return (cn && cn.match(cName.word2regexp()));
    };
    /**
     *  html要素にclassを追加する
     *  @param  String  cName
     */
    this.addClass = function(cName) {
        if (!this.hasClass(cName)) {
            elm.className += " " + cName;
        }
    };
    /**
     *  html要素からclassを削除する
     *  @param  String  cName
     */
    this.removeClass = function(cName) {
        elm.className = elm.className.replace(cName.word2regexp(), " ").trim();
    };
    /**
     *  html要素のclassを変更する
     *  @param  String  oldClass
     *  @param  String  newClass
     */
    this.convertClass = function(oldClass, newClass) {
        this.removeClass(oldClass);
        this.addClass(newClass);
    };
};


/***** Pryn.Init *****/

/**
 *  表をシマシマにする（奇数番目のtr要素のclassに stripe を追加する）
 *  シマシマにしたくない行（見出し行など）はthead要素で囲っておくこと
 *  @param  HTMLElement table    table要素
 */
Pryn.Init.stripeTable = function(table) {
    if (!(new Pryn.ClassAccessor(table)).hasClass("stripes")) {
        return;
    }
    var rows = table.getElementsByTagName("tbody")[0].getElementsByTagName("tr");
    for (var i = 0, len = rows.length; i < len; i += 2) {
        (new Pryn.ClassAccessor(rows[i])).addClass("stripe");
    }
};
/**
 *  formの子要素のstyle等の設定
 *  CSSにてclass "clickable" "focused" のstyleを定義すること
 *  @param  HTMLElement fElm    formの子要素
 */
Pryn.Init.setStyles = function(fElm) {
    var acs = new Pryn.ClassAccessor(fElm);
    if (acs.hasClass("ready")) {
        fElm.focus();               //デフォルトでfocusを当てる
    }
    var type = fElm.type;
    if (fElm.tagName == 'TEXTAREA' || type.match(/^(text|password)$/i)) {
        Pryn.Init.setTextStyle(fElm, acs);
    } else if (fElm.tagName == 'INPUT' && type != "hidden") {
        acs.addClass("clickable");  //ファイルアップロードはFirefoxは非対応
        if (type == "image") {
            Pryn.Init.setSmartImage(fElm);
            Pryn.Init.inputImages.push(fElm);
        }
    }
};
/**
 *  テキスト入力系要素のstyle等の設定
 *  @param  HTMLElement         fElm    formの子要素
 *  @param  Pryn.ClassAccessor  acs
 */
Pryn.Init.setTextStyle = function(fElm, acs) {
    Pryn.addEvent(fElm, "focus", function() {   //フォーカスされた時
        if (!fElm.readOnly) {
            acs.addClass("focused");
        }
        if (acs.hasClass("hint")) {             //ヒント付テキストボックス
            this.value = "";
            acs.convertClass("hint", "ex-hint");
        } else if (acs.hasClass("copy")) {      //コピー用テキスト
            setTimeout(function() { fElm.select(); }, 100); //selectし損ねるのを防ぐ
        }
    });
    Pryn.addEvent(fElm, "blur", function() {    //フォーカスが外れた時
        acs.removeClass("focused");
        if (acs.hasClass("ex-hint") && !fElm.value) {   //ヒント付テキストボックスで未入力
            acs.convertClass("ex-hint", "hint");
            fElm.value = fElm.title;
        }
    });
    if (acs.hasClass("hint")) {                 //ヒント付テキストボックス
        if (fElm.value && fElm.value != fElm.title) {   //既に値がある場合
            acs.convertClass("hint", "ex-hint");
        } else {
            fElm.value = fElm.title;            //title属性の値を表示
        }
    } else if (acs.hasClass("copy")) {          //コピー用テキスト
        fElm.readOnly = true;
    }
};
/**
 *  別Windowで開くリンク・formの設定
 *  @param  HTMLElement link    a要素 or form要素（自動判断にはhref属性が必要）
 */
Pryn.Init.setTarget = function(link) {
    var acs = new Pryn.ClassAccessor(link);
    var ext = false;
    if (Pryn.Init.keepThisSite && !acs.hasClass("same-window")) {
        var next = Pryn.getDomain(link);
        ext = (next && next != location.hostname);   //外部へのリンクかどうか
    }
    if (ext || acs.hasClass("open")) {
        link.target = "_blank";
    }
};
/**
 *  formの二重送信防止設定
 *  @param  HTMLElement frm
 */
Pryn.Init.setSmartSubmit = function(frm) {
    Pryn.addEvent(frm, "submit", function() {
        Pryn.Init.setSubmitStyles.foreach(this);
       //imageボタンはformの子要素にならないので別途処理
        Pryn.Init.setSubmitStyles.foreach(Pryn.Init.inputImages);
    });
    frm.href = frm.action;          //別Windowで開くための前処理
    Pryn.Init.setTarget(frm);       //必要に応じて別Windowで開く
};
/**
 *  formのsubmit時の処理
 *  @param  HTMLElement formの子要素
 */
Pryn.Init.setSubmitStyles = function(fElm) {
    if ((new Pryn.ClassAccessor(fElm)).hasClass("hint")) {  //送信前にヒントをクリア
        fElm.value = "";
    }
    //textareaはFirefoxで戻ったときに初期値が消えるので除外
    if (fElm.disabled || fElm.tagName == "TEXTAREA") {
        return;
    }
    //操作不可にして、しばらくしたら戻す
    setTimeout(function() { fElm.disabled = true; }, 1);
    setTimeout(function() { fElm.disabled = false; }, Pryn.Init.frozenTime * 1000);
    Pryn.Init.frozen.push(fElm);
};
/**
 *  label要素のクロスブラウザ対応（Safari2以前は非対応）
 *  @param  HTMLElement label
 */
Pryn.Init.setSmartLabel = function(label) {
    if (!label.htmlFor) {
        var input = label.getElementsByTagName("INPUT")[0];
        if (!input) {
            return;
        } else if (!input.id) {
            input.id = Pryn.Init.labelForIdPrefix + Pryn.Init.labelForIdNo++;
        }
        label.htmlFor = input.id;   //IE6ではfor属性の省略ができない
    }
    //IEはlabel要素内のimg要素をクリックしてもチェックが付かない
    var images = label.getElementsByTagName("IMG");
    var len = images.length;
    if (!len) {
        return;
    }
    var clickIt = function() { document.getElementById(label.htmlFor).click(); };
    for (var i = 0; i < len; i++) {
        Pryn.addEvent(images[i], "click", clickIt);
    }
};
/**
 *  img要素のtitleとstyleの設定
 *  CSSにてclass "clickable" のstyleを定義すること
 *  @param  HTMLElement img
 */
Pryn.Init.setSmartImage = function(img) {
    //title属性をセット（IEの動きに合わせる）
    if (!img.title && img.alt) {
        img.title = img.alt;
    }
    if (img.onclick) {
        (new Pryn.ClassAccessor(img)).addClass("clickable");
    }
};

/**
 *  初期処理（見た目の影響のあるものを優先）
 */
Pryn.addEvent(window, "load", function() {
    Pryn.Init.stripeTable.foreach($T("table"));
    
    Pryn.Init.setStyles.foreach($T("input"));
    Pryn.Init.setStyles.foreach($T("textarea"));
    Pryn.Init.setStyles.foreach($T("button"));

    Pryn.Init.setTarget.foreach($T("a"));

    Pryn.Init.setSmartSubmit.foreach($T("form"));

    Pryn.Init.setSmartLabel.foreach($T("label"));

    Pryn.Init.setSmartImage.foreach($T("img"));
});
/**
 *  formの二重送信防止の凍結解除
 */
Pryn.addEvent(window, "unload", function() {
    (function(fElm) { fElm.disabled = false; }).foreach(Pryn.Init.frozen);
});
